package cn.com.cetccst.devicerestriction;

import android.app.AlarmManager;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;

import androidx.lifecycle.LiveData;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.com.cetccst.datacenter.policy.PolicyRepository;
import cn.com.cetccst.datacenter.policy.entity.PolicyEntity;
import cn.com.cetccst.datacenter.policy.entity.RestrictionEntity;
import cn.com.cetccst.datacenter.policy.entity.RestrictionStatusEntity;
import cn.com.cetccst.datacenter.policy.fence.GeoFenceArea;
import cn.com.cetccst.datacenter.policy.fence.TimeFence;
import cn.com.cetccst.devicerestriction.timefence.TimeFenceManager;
import cn.com.cetccst.mdm.MDMManager;
import cn.com.cetccst.mdm.engine.IMDMEngine;
import cn.com.cetccst.tools.log.CommonLogger;

public class DeviceRestrictionManager {

    private static final String TAG = "DeviceRestriction";
    private TimeFenceManager mTimeFenceManager;
    private Context mContext;

    //缓存一份期望的策略状态，已经判断过时间围栏状态。
    private Map<String, Boolean> expectStatusCache = new HashMap<>();

    private static final String FIELD_RESTRICTIONS = "restrictions";
    private static final String FIELD_VALUE = "value";
    private static final String FIELD_VALID_TIME = "validTime";
    private static final String FIELD_DAY_OF_WEEK = "dayOfWeek";
    private static final String FIELD_TIME_FRAMES = "timeFrames";
    private static final String FIELD_START = "start";
    private static final String FIELD_END = "end";

    private static final String FIELD_VALID_LOCATION = "validLocation";
    private static final String FIELD_NAME = "name";
    private static final String FIELD_ACCURACY = "accuracy";
    private static final String FIELD_FREQUENCY = "frequency";
    private static final String FIELD_AREAS = "areas";
    private static final String FIELD_TITLE = "title";
    private static final String FIELD_SHAPE = "shape";
    private static final String FIELD_OUTLINE = "outline";
    private static final String KEY_CIRCLE = "circle";
    private static final String FIELD_CENTER = "center";
    private static final String FIELD_LNG = "lng";
    private static final String FIELD_LAT = "lat";
    private static final String FIELD_RADIUS = "radius";


    private static DeviceRestrictionManager INSTANCE = null;

    public static DeviceRestrictionManager getInstance() {
        return INSTANCE;
    }

    public DeviceRestrictionManager(Context context) {
        mContext = context;
        mTimeFenceManager = new TimeFenceManager();

        periodCheck();

        LiveData<PolicyEntity> policy = PolicyRepository.getInstance().getPolicy(PolicyEntity.POLICY_RESTRICTION);
        //动态监听合规性策略的变化，从而驱动UI和事件的执行
        policy.observeForever(policyEntity -> {
            CommonLogger.info("load restrictions data from DB");
            Log.e(TAG, "received policy : " + (policyEntity != null ? policyEntity.getPolicyName() + " / " + policyEntity.getContent() : null));
            initRestrictions(context, policyEntity);
        });
    }

    public static void initialize(Context context) {
        INSTANCE = new DeviceRestrictionManager(context);
    }

    private void periodCheck() {
        config();
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + DateUtils.MINUTE_IN_MILLIS, "restriction_check", this::periodCheck, null);
    }


    public void configRestriction(@RestrictionName String restriction, boolean disabled, List<TimeFence> timeFence) {
        if (timeFence != null) {
            if (disabled) {
                //没有在时间围栏范围内，则不禁用。
                boolean isInTimeFence = mTimeFenceManager.isInTimeFence(timeFence);
                if (!isInTimeFence) {
                    disabled = false;
                }
            }

        }
        Log.i(TAG, String.format("config :%s to disable:%b and timefence enabled:%b", restriction, disabled, timeFence));

        expectStatusCache.put(restriction, disabled);
        IMDMEngine engine = MDMManager.getInstance().getMdmEngine();
        switch (restriction) {
            case Restriction.CAMERA:
                engine.controlCamera(disabled);
                break;
            case Restriction.MICROPHONE:
                engine.controlMicroPhone(disabled);
                break;
            case Restriction.SDCARD:
                engine.controlExternalStorage(disabled);
                break;
            case Restriction.SCREEN_SHOT:
                engine.controlScreenShot(disabled);
                break;
            case Restriction.AUTO_TIME:
                engine.controlAutoTime(disabled);
                break;
            case Restriction.USB_TRANSFER:
                engine.controlUsbData(disabled);
                break;
            default:
                Log.e(TAG, "config retriction can handle restriction: " + restriction);
        }
        saveRestrictionStatus();
    }

    public boolean isDisabled(@RestrictionName String restriction) {
        IMDMEngine engine = MDMManager.getInstance().getMdmEngine();
        switch (restriction) {
            case Restriction.CAMERA:
                return engine.isCameraControlled();
            case Restriction.MICROPHONE:
                return engine.isMicroPhoneControlled();
            case Restriction.SDCARD:
                return engine.isExternalStorageControlled();
            case Restriction.SCREEN_SHOT:
                return engine.isScreenShotControlled();
            case Restriction.AUTO_TIME:
                return engine.isAutoTimeControlled();
            case Restriction.USB_TRANSFER:
                return engine.isUsbDataControlled();
            default:
                Log.e(TAG, "get restriction status can handle restriction: " + restriction);
        }

        return false;
    }

    public void config() {
        if (MDMManager.getInstance() == null) {
            Log.e(TAG, "mdm engine did not initialized");
            return;
        }

        IMDMEngine engine = MDMManager.getInstance().getMdmEngine();
        if (engine == null) {
            Log.e(TAG, "can not restrict by null mdm engine");
            return;
        }

        PolicyRepository.getInstance().getAllRestriction().observeForever(restrictionEntities -> {

            expectStatusCache.clear();
            for (RestrictionEntity restriction : restrictionEntities) {
                boolean disable = restriction.getValue() == 0;
                Log.e(TAG, "config restriction : " + restriction.getName() + " / " + disable);
                configRestriction(RestrictionModel.valueOf(restriction.getName()).name, disable, restriction.getTimeFence());
            }
        });
    }


    private void initRestrictions(Context context, PolicyEntity policyEntity) {
        if (policyEntity != null && !TextUtils.isEmpty(policyEntity.getContent())) {
            List<RestrictionEntity> restrictionList = new ArrayList<>();
            JSONObject restrictions = null;
            try {
                JSONObject complianceObj = new JSONObject(policyEntity.getContent());
                restrictions = new JSONObject(complianceObj.optString(FIELD_RESTRICTIONS));
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }

            for (RestrictionModel model : RestrictionModel.values()) {
                JSONObject restriction = restrictions.optJSONObject(model.name());
                if (restriction == null) {
                    CommonLogger.warn("not found define of " + model.name());
                    continue;
                }
                try {
                    int value = restriction.getInt(FIELD_VALUE);
                    RestrictionEntity entity = new RestrictionEntity();
                    entity.setName(model.name);
                    entity.setValue(value);

                    JSONArray validTimes = restriction.optJSONArray(FIELD_VALID_TIME);
                    if (validTimes != null && validTimes.length() > 0) {
                        entity.setTimeFence(parseTimeFence(validTimes));
                    }

                    JSONObject validLocation = restriction.optJSONObject(FIELD_VALID_LOCATION);
                    if (validLocation != null) {
                        entity.setGeoFence(parseGeoFence(validLocation));
                    }
                    restrictionList.add(entity);


                } catch (JSONException e) {
                    throw new RuntimeException(e);
                }
            }
            PolicyRepository.getInstance().saveAllRestriction(restrictionList);
        }
    }

    public List<TimeFence> parseTimeFence(JSONArray validTimes) throws JSONException {
        if (validTimes == null) {
            return null;
        }
        List<TimeFence> timeFenceList = new ArrayList<>();
        for (int i = 0; i < validTimes.length(); i++) {
            JSONObject validTime = validTimes.getJSONObject(i);
            int dayOfWeek = validTime.getInt(FIELD_DAY_OF_WEEK);
            JSONArray timeFrames = validTime.getJSONArray(FIELD_TIME_FRAMES);
            for (int j = 0; j < timeFrames.length(); j++) {
                JSONObject timeFrame = timeFrames.getJSONObject(j);
                String startTime = timeFrame.getString(FIELD_START);
                String endTime = timeFrame.getString(FIELD_END);
                if ("24:00".equals(endTime)) {
                    endTime = "23:59";
                }
                TimeFence timeFenceBean = new TimeFence(dayOfWeek, startTime, endTime);
                timeFenceList.add(timeFenceBean);
            }
        }
        return timeFenceList;
    }


    public List<GeoFenceArea> parseGeoFence(JSONObject validLocation) throws JSONException {
        if (validLocation == null) {
            return null;
        }
        List<GeoFenceArea> geoFenceList = new ArrayList<>();
        String policyName = validLocation.getString(FIELD_NAME);
        int accuracy = validLocation.getInt(FIELD_ACCURACY);
        int frequency = validLocation.getInt(FIELD_FREQUENCY);
        JSONArray areasArray = validLocation.getJSONArray(FIELD_AREAS);
        for (int i = 0; i < areasArray.length(); i++) {
            JSONObject areaJSON = areasArray.getJSONObject(i);

            String title = areaJSON.getString(FIELD_TITLE);
            String shape = areaJSON.getString(FIELD_SHAPE);
            if (shape.equals(KEY_CIRCLE)) {
                String outline = areaJSON.getString(FIELD_OUTLINE);
                JSONObject outlineJSON = new JSONObject(outline);

                JSONObject centerJSON = outlineJSON.getJSONObject(FIELD_CENTER);
                Double lng = centerJSON.getDouble(FIELD_LNG);
                Double lat = centerJSON.getDouble(FIELD_LAT);
                int radius = outlineJSON.getInt(FIELD_RADIUS);
                String center = lat + "," + lng;

                GeoFenceArea geoFenceEntity = new GeoFenceArea();
                geoFenceEntity.setPolicyName(policyName);
                geoFenceEntity.setFenceName(title);
                geoFenceEntity.setType(shape);
                geoFenceEntity.setRange(radius);
                geoFenceEntity.setCenter(center);
                geoFenceEntity.setAccuracy(accuracy);
                geoFenceEntity.setFrequency(frequency);
                geoFenceList.add(geoFenceEntity);
                CommonLogger.info(TAG + ":parsed geo fence entity: " + geoFenceEntity.toString());
            }
        }
        return geoFenceList;
    }

    //存储当前策略期望状态和本地执行状态
    public void saveRestrictionStatus() {
        List<RestrictionStatusEntity> statuses = new ArrayList<>();
        for (RestrictionModel model : RestrictionModel.values()) {
            RestrictionStatusEntity status = new RestrictionStatusEntity();

            status.setName(model.name);
            status.setActual(isDisabled(model.name) ? 0 : 1);
            status.setExpected(expectStatusCache.containsKey(model.name) ? 0 : 1);

            statuses.add(status);
        }
        PolicyRepository.getInstance().saveAllRestrictionStatus(statuses);
    }
}
